package com.sunck.common.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.sunck.common.shiro.*;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.Filter;

/**
 * Shiro 配置类
 *
 * @author LengChen
 * @version 1.0
 * @date 2020/8/5
 */

@Configuration
public class ShiroConfig {

    /**
     * 登录地址
     */
    @Value("${shiro.filterManager.loginUrl}")
    private String loginUrl;

    /**
     * 权限验证错误URL
     */
    @Value("${shiro.filterManager.unauthorizedUrl}")
    private String unauthorizedUrl;

    /**
     * 验证码类型
     */
    @Value("${shiro.filterManager.captchaEnabled}")
    private boolean captchaEnabled;

    /**
     * 验证码类型
     */
    @Value("${shiro.filterManager.captchaType}")
    private String captchaType;

    /**
     * Session超时时间，单位为分钟（默认30分钟）
     */
    @Value("${shiro.session.timeout}")
    private int timeout;

    /**
     * Session定时清理时间，单位为分钟（默认30分钟）
     */
    @Value("${shiro.session.timeoutClean}")
    private int timeoutClean;

    /**
     * 设置缓存类型(redis、ehCache)
     */
    @Value("${shiro.cacheType}")
    private String cacheType;

    /**
     * 设置缓存前缀
     */
    @Value("${spring.redis.keyPrefix}")
    private String keyPrefix;

    /**
     * 设置Cookie的域名
     */
    @Value("${shiro.cookie.domain}")
    private String domain;

    /**
     * 设置cookie的有效访问路径
     */
    @Value("${shiro.cookie.path}")
    private String path;

    /**
     * 设置HttpOnly属性
     */
    @Value("${shiro.cookie.httpOnly}")
    private boolean httpOnly;

    /**
     * 设置Cookie的过期时间，秒为单位
     */
    @Value("${shiro.cookie.maxAge}")
    private int maxAge;

    /**
     * 设置cipherKey密钥
     */
    @Value("${shiro.cookie.cipherKey}")
    private String cipherKey;


    /**
     * 自定义Realm
     */
    @Bean
    UserRealm userRealm()
    {
        UserRealm userRealm = new UserRealm();
        userRealm.setCacheManager(getCacheManager());
        return userRealm;
    }

    /**
     * 安全管理器 配置主要是Realm的管理认证
     */
    @Bean
    SecurityManager securityManager()
    {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置realm.
        securityManager.setRealm(userRealm());
        // 记住我
       //securityManager.setRememberMeManager(rememberMeManager());
        // 注入缓存管理器;
        securityManager.setCacheManager(getCacheManager());
        // session管理器
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }



    /**
     *Shiro过滤器配置
     *
     * @return
     */
    @Bean
    ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // Shiro的核心安全接口,配置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //配置默认登录页（认证失败后跳此页）
        shiroFilterFactoryBean.setLoginUrl(loginUrl);
        //登录成功后跳转页
        shiroFilterFactoryBean.setSuccessUrl("/index");
        // 权限认证失败，则跳转到指定页面
        shiroFilterFactoryBean.setUnauthorizedUrl(loginUrl);
        // Shiro权限过滤过滤器定义
        Map<String, String> shiroFilterChainDefinitions = new LinkedHashMap<>();

        // 对静态资源设置匿名访问
        shiroFilterChainDefinitions.put("/css/**", "anon");
        shiroFilterChainDefinitions.put("/docs/**", "anon");
        shiroFilterChainDefinitions.put("/fonts/**", "anon");
        shiroFilterChainDefinitions.put("/img/**", "anon");
        shiroFilterChainDefinitions.put("/ajax/**", "anon");
        shiroFilterChainDefinitions.put("/js/**", "anon");
        shiroFilterChainDefinitions.put("/static/**", "anon");
        shiroFilterChainDefinitions.put("/captcha", "anon");//验证码

        shiroFilterChainDefinitions.put("/", "anon");

        // 退出 logout地址，shiro去清除session
        shiroFilterChainDefinitions.put("/logout", "logout");
        // 登录拦截器
        shiroFilterChainDefinitions.put("/login", "authc");


        //自定义拦截器
        Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();
        filters.put("authc", new SunckFormAuthenticationFilter());

        // 所有请求需要认证
        shiroFilterChainDefinitions.put("/**", "user");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(shiroFilterChainDefinitions);
        return shiroFilterFactoryBean;


    }

//    /**
//     * 自定义验证码过滤器
//     */
//    @Bean
//    public CaptchaValidateFilter captchaValidateFilter()
//    {
//        CaptchaValidateFilter captchaValidateFilter = new CaptchaValidateFilter();
//        captchaValidateFilter.setCaptchaEnabled(captchaEnabled);
//        captchaValidateFilter.setCaptchaType(captchaType);
//        return captchaValidateFilter;
//    }

    /**
     * 自定义会话管理配置
     * @return
     */
    @Bean
    public SessionManager  sessionManager() {
        SessionManager sessionManager = new SessionManager();
        sessionManager.setSessionDAO(sessionDAO());
        //会话超时时间（单位：毫秒）
        sessionManager.setGlobalSessionTimeout(timeout * 60 * 1000);
        //定时清理失效会话, 清理用户直接关闭浏览器造成的孤立会话（单位：毫秒）
        sessionManager.setSessionValidationInterval(timeoutClean * 60 * 1000);
        //是否定时检查session
        sessionManager.setSessionValidationSchedulerEnabled(false);
        //指定本系统SESSIONID, 默认为: JSESSIONID 问题: 与SERVLET容器名冲突, 如JETTY, TOMCAT 等默认JSESSIONID,
        //当跳出SHIRO SERVLET时如ERROR-PAGE容器会为JSESSIONID重新分配值导致登录会话丢失!
        sessionManager.setSessionIdCookie(sessionIdCookie());
        sessionManager.setSessionIdCookieEnabled(true);
        return  sessionManager;
    }

    /**
     * shiro缓存管理
     * @return
     */
    @Bean
    public CacheManager getCacheManager(){
        if("redis".equals(cacheType)){
            RedisCacheManager redisCacheManager = new RedisCacheManager();
            redisCacheManager.setCacheKeyPrefix(keyPrefix+"_shiro_cache_");
            return redisCacheManager;
        }else{
            //EhCache缓存
            return null;
        }
    }

    /**
     * 指定本系统sessionid, 问题: 与servlet容器名冲突, 如jetty, tomcat 等默认jsessionid,
     * 当跳出shiro servlet时如error-page容器会为jsessionid重新分配值导致登录会话丢失!
     *
     * @return
     */
    @Bean
    public SimpleCookie sessionIdCookie() {
        SimpleCookie simpleCookie = new SimpleCookie();
        simpleCookie.setName("sunck.session.id");
        simpleCookie.setDomain(domain);
        simpleCookie.setPath(path);
        simpleCookie.setHttpOnly(httpOnly);
        simpleCookie.setMaxAge(maxAge * 24 * 60 * 60);
        return simpleCookie;
    }

    /**
     * 自定义sessionDAO会话
     */
    @Bean
    public RedisSessionDao sessionDAO()
   {
       RedisSessionDao sessionDAO = new RedisSessionDao(timeout * 60 * 1000 ,keyPrefix);
       return sessionDAO;
    }

    /**
     * thymeleaf模板引擎和shiro框架的整合
     */
    @Bean
    public ShiroDialect shiroDialect()
    {
        return new ShiroDialect();
    }

    /**
     * 开启Shiro注解通知器
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
            @Qualifier("securityManager") SecurityManager securityManager)
    {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
        proxyCreator.setProxyTargetClass(true);
        return proxyCreator;
    }

}
